﻿/********************************************************************
*                                                                   *
* Microsoft Windows Sidebar Gadget for NNTT.ORG                     *
* Released under the MIT license by Alexey Glukharev, Januar 2012.  *
*                                                                   *
********************************************************************/

//----------------------------------------
// Global variables
//---
var majorVersion = 1;
var minorVersion = 0;

var size =[ "B", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"];
var month =[ "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"];
var status =[
"<b style='color: #C71585;'>*</b>", /* not_checked */
"<b style='color: #FF4500;'>x</b>", /* closed */
"<b style='color: #008000;'>&radic;</b>", /* checked */
"<b style='color: #0000FF;'>D</b>", /* duplicate */
"<b style='color: #FF4500;'>!</b>", /* no_desc */
"<b style='color: #FF0000;'>?</b>", /* need_edit */
"<b style='color: #D26900;'>#</b>", /* consumed */
"<b style='color: #CE3800;'>&copy;</b>", /* closed_copy */
"<b style='color: #FF0000;'>&sum;</b>", /* absorbed */
"<b style='color: #0000FF;'>T</b>", /* temp */
"<b style='color: #2424FF;'>%</b>" /* in_prograss */];

var status_text =[
"not_checked",
"closed",
"checked",
"duplicate",
"no_desc",
"need_edit",
"consumed",
"closed_copy",
"absorbed",
"temp",
"in_prograss"];

var timerCount;
var refreshTimer;
var togglePanelsTimer;
var lastOpenedLink;
var torrentInfo;

//----------------------------------------
// Enable crossdomain interaction
//---
jQuery.support.cors = true;

//----------------------------------------
// Script entry point
//---
$(document).ready(function () {
    initSettings();
    initFlyout();
    
    System.Gadget.onDock = dockStateChanged;
    System.Gadget.onUndock = dockStateChanged;
    dockStateChanged();
    
    initPanels();
    initStatistic();
    checkUpdates();
});

//----------------------------------------
// Handlers
//---
function dockStateChanged() {
    if (System.Gadget.docked) {
        var width = '130px';
        var height = '105px';
        
        $('#background').css('width', width).css('height', height).attr('src', './images/docked-background.png');
        $(document.body).removeClass('undocked').addClass('docked').css('width', width).css('height', height);
    } else {
        var width = '260px';
        var height = '240px';
        
        $('#background').css('width', width).css('height', height).attr('src', './images/undocked-background.png');
        $(document.body).removeClass('docked').addClass('undocked').css('width', width).css('height', height);
    }
}

function settingsClosed(e) {
    if (e.closeAction == e.Action.commit) {
        // Refresh settings data
        Settings().read();
        
        // Restart timers with new settings
        refreshPanels();
        setPanelsToggleTimer();
        //   initStatistic();
    }
}

function togglePanels(prevPanel) {
    prevPanel = prevPanel | false;
    
    var curr = $(".info-panel:visible:first");
    if (curr.length == 0) {
        // Docked mode - all panels are invisible
        return;
    }
    
    var next;
    if (prevPanel) {
        next = $(curr).prev(":not(.skipByToggle)");
        if (next.length == 0) {
            next = $(".info-panel").not(":visible").filter(":not(.skipByToggle):last");
        }
    } else {
        next = $(curr).next(":not(.skipByToggle)");
        if (next.length == 0) {
            next = $(".info-panel").not(":visible").filter(":not(.skipByToggle):first");
        }
    }
    
    $(curr).fadeOut(100, function () {
        $(next).fadeIn(100);
    });
    
    $("#panel-name").html($(next).data("title"));
    
    // Сохраним последнюю отображённую панель, чтобы
    // отобразить её при следующем запуске
    // Это позволит отображать выбранную пользователем
    // вкладку при отключенном автоматическом переключении вкладок
    Settings().set('startupPanel', $(next).attr('id'));
    Settings().save();
}

function onFlyoutHide() {
    $(".show-in-flyout").removeClass("show-in-flyout");
}

//----------------------------------------
// Initialisation functions
//---
function initFlyout() {
    System.Gadget.Flyout.file = "flyout.html";
    System.Gadget.Flyout.onHide = onFlyoutHide;
}

function initSettings() {
    // Set settings UI
    System.Gadget.settingsUI = "settings.html";
    System.Gadget.onSettingsClosed = settingsClosed;
}

function initPanels() {
    
    $("#prev-panel-btn, #next-panel-btn").click(function () {
        togglePanels($(this).is("[id=prev-panel-btn]"));
        // To avoid a situation when the panel is switched by the timer
        // right after switchings by the user, we reset the timer
        // in the moment of pressing of the button
        setPanelsToggleTimer();
        return false;
    });
    
    // Initiate the "interactive" buttons
    setHoverEffect($("#prev-panel-btn img:first, #next-panel-btn img:first"));
    setHoverEffect($("#refresh-btn img:first"));
    setHoverEffect($(".social-icon, .social-icon-mini"));
    
    $("#refresh-btn").click(function () {
        refreshPanels();
        return false;
    });
    
    // Disable panel switching for a moment while cursor is over panel's area
    // It allow to avoid unnecessary switching during reading
    $(".info-panel").hover(function () {
        clearInterval(togglePanelsTimer);
    },
    function () {
        setPanelsToggleTimer();
    });
    
    // Remove panels attribute "title", save it
    // preliminary in an internal variable
    $(".info-panel").each(function (i) {
        $(this).data("title", $(this).attr("title"));
        $(this).removeAttr("title");
    });
    
    // Init search submit action
    $("#quick-search").submit(function () {
        if (System.Gadget.docked) {
            // In docked state search type is determined by the settings
            $(this).attr('action', Settings().get('defaultSearchType'));
        } else {
            $(this).attr('action', $("input[name='search-type']:radio:checked").val());
        }
        
        var txt = $('#search-text').val();
        return !(txt == strings[ 'search'] || ! txt);
    });
    
    // Init search button action
    $("#search-submit").click(function () {
        $("#quick-search").submit();
        return false;
    });
    
    // Восстановим последнюю отображённую панель
    var startup = Settings().get('startupPanel');
    $('#' + startup).css('display', 'block');
    $("#panel-name").html($('#' + startup).data("title"));
    
    // Initialize panels content and all timers
    refreshPanels();
    setPanelsToggleTimer();
}

function initStatistic() {
    // Для отрисовки графика нужно указать размер в развёрнутом виде,
    // в то время как в данный момент виджет свёрнут. Зададим этот размер
    // вручную.
    $("#torrent").css({
        width: 240
    });
    
    // Создаём объект провайдера данных для статитсики
    // TODO: организовать код получше (фабрика классов?...)
    var provider;
    var defProfile = Settings().get('defaultProfile');
    if (defProfile.length > 0) {
        // Имеется выбранный профиль. Постараемся его применить
        var profile = Settings().getProfileManager().get(defProfile);
        switch (profile.client) {
            case "utorrent": {
                provider = new CuTorrentDataProvider(profile);
                break;
            }
            
            case "transmission": {
                provider = new CTransmissionDataProvider(profile);
                break;
            }
            
            default: {
                System.Debug.outputString("Unknown client: " + profile.client);
            }
        }
        
        // Создаём объект информера статистики торрент-клиента
        torrentInfo = new CTorrentInfo($("#torrent"), provider,
        function () {
            $("#torrent").html("<span class='center-wrapper'><span><p class='load-message'><img src='images/error.png' class='gadget-icon'/>" + "Не удалось получить данные.<br/>Вероятно клиент не запущен<br/>или неверно настроен." + "</p></span></span>");
        });
        
        // Удалим атрибут проспуска при пролистывании
        // Он мог остаться с момента последней настройки
        $("#torrent").removeClass("skipByToggle");
    } else {
        // Профилей не создано
        // Не будем отображать вкладку "Торрент", чтобы не смущать пользователя
        $("#torrent").addClass("skipByToggle");
    }
}

//----------------------------------------
// Ajax functions
//---
function checkUpdates() {
    $.ajax({
        cache: false,
        async: true,
        url: "http://www.nntt.org/help/nnttinformer/update.xml",
        dataType: "xml",
        success: function (msg) {
            var version = $(msg).find("lastVersion").get(0);
            if (version != null && version != undefined) {
                // Parse xml data
                var major = parseInt($(version).find("major").text());
                var minor = parseInt($(version).find("minor").text());
                var link = $(version).find("url").text();
                if ((major > majorVersion) || ((major == majorVersion) && (minor > minorVersion))) {
                    
                    // If there is a newer version, add a line "New version!" and reduce the size of icons
                    // Change the space between them to fit this text
                    $(".social-icon").css({
                        width: "25px",
                        height: "25px"
                    });
                    
                    $(".social-td").css("padding", "2px 3px");
                    $("#update-panel").html("<a type='application/x-windows-gadget' id='get-update' href='" + link + "' title='" + strings[ 'updateDescription'] + "'>" + strings[ 'updateAvailable'] + "</a>");
                    
                    animateText();
                }
            }
        }
    });
}

function getReleases(fids, count) {
    return $.ajax({
        cache: false,
        type: "POST",
        url: "http://www.nntt.org/ajax.php",
        data: {
            action: "latest_torrents", "fid[]": fids, count: count, "get_forums": false
        },
        dataType: "json"
    });
}

function getNews(forum_id, count) {
    return $.ajax({
        cache: false,
        type: "POST",
        url: "http://www.nntt.org/ajax.php",
        data: {
            action: "latest_news", forum_id: forum_id, count: count
        },
        dataType: "json"
    });
}

//----------------------------------------
// Working with timers
//---
function updateCounter() {
    timerCount--;
    if (timerCount <= 0) {
        refreshPanels();
    }
    
    // Form a line of time to update
    var hours = Math.floor(timerCount / 3600);
    var minutes = Math.floor(timerCount / 60) - hours * 60;
    var seconds = timerCount - minutes * 60 - hours * 3600;
    
    var time = "";
    if (hours > 0) {
        time += ((String(hours).length == 2)? hours: "0" + hours) + ":";
    }
    time += ((String(minutes).length == 2)? minutes: "0" + minutes) + ":";
    time += ((String(seconds).length == 2)? seconds: "0" + seconds);
    
    $("#timer").html(time);
}

function setDataRefreshTimer() {
    timerCount = parseInt(Settings().get('dataRefreshPeriod'));
    clearInterval(refreshTimer);
    refreshTimer = setInterval(updateCounter, 1000);
}

function setPanelsToggleTimer() {
    var panelsTogglePeriod = parseInt(Settings().get('panelsTogglePeriod'));
    clearInterval(togglePanelsTimer);
    if (panelsTogglePeriod != - 1) {
        togglePanelsTimer = setInterval(togglePanels, panelsTogglePeriod * 1000);
    }
}

function refreshPanels() {
    setDataRefreshTimer();
    refreshNews();
    refreshReleases();
}

//----------------------------------------
// Working with DOM
//---
function refreshReleases() {
    if (System.Gadget.visible) {
        $("#releases").html("<span class='center-wrapper'><span><p class='load-message'><img src='images/load.gif' class='gadget-icon'/>" + strings[ 'loading'] + "</p></span></span>");
        
        // Try to load a saved list of foru
        var forumsArray = Settings().get("forumsList");
        
        // Are there forums?
        if (forumsArray.length > 0 && forumsArray[0].length > 0) {
            // Request the list of releases for the selected forums
            $.when(getReleases(forumsArray, 7))
            .done(function (msg) {
                // Now there is animation of download - remove it
                $("#releases").html("");
                
                // Are there releases?
                if (msg.data.length > 0) {
                    // Fill list of releases
                    for (var i = 0; i < msg.data.length; i++) {
                        $('<div/>', {
                            "class": "panel-prefix reelease-status",
                            html: status[msg.data[i].status]
                        })
                        .appendTo($("#releases"));
                        
                        $('<div>', {
                            "class": "panel-body release-body",
                            html: $("<a/>", {
                                href: "#",
                                "title": msg.data[i].topic_title,
                                html: msg.data[i].topic_title,
                                data: {
                                    "msg": msg.data[i]
                                },
                                click: function () {
                                    // Unselect all links, if eny exist
                                    $(".show-in-flyout").removeClass("show-in-flyout");
                                    
                                    // Mark selected link
                                    $(this).addClass("show-in-flyout");
                                    
                                    // Bind opening of flyout window to link
                                    fillFlyoutAndShow($(this).data("msg"));
                                    return false;
                                }
                            })
                        })
                        .appendTo($("#releases"));
                    }
                } else {
                    $("#releases").html("<span class='center-wrapper'><span><p class='load-message'><img src='images/info.png' class='gadget-icon'/>" + strings[ 'noDataToShow'] + "</p></span></span>");
                }
            })
            .fail(function (xhr, status, error) {
                System.Debug.outputString("NNTT.ORG Informer: Unable to get releases. Reason: " + xhr.statusText);
                $("#releases").html("<span class='center-wrapper'><span><p class='load-message'><img src='images/error.png' class='gadget-icon'/>" + strings[ 'error'] + xhr.statusText + "</p></span></span>");
            });
        } else {
            $("#releases").html("<span class='center-wrapper'><span><p class='load-message'><img src='images/info.png' class='gadget-icon'/>" + strings[ 'forumsNotSet'] + "</p></span></span>");
        }
    }
}

function refreshNews() {
    if (System.Gadget.visible) {
        $("#news").html("<span class='center-wrapper'><span><p class='load-message'><img src='images/load.gif' class='gadget-icon'/>" + strings[ 'loading'] + "</p></span></span>");
        
        $.when(getNews(2, 7), getNews(590, 7), getNews(1407, 7), getNews(2082, 7))
        .done(function () {
            // Iterate through objects returned by the ajax-requests
            var news = new Array();
            for (var i = 0; i < arguments.length; i++) {
                for (var j = 0; j < arguments[i][0].data.length; j++) {
                    news.push(arguments[i][0].data[j]);
                }
            }
            
            // Sort by date of news
            news.sort(function (a, b) {
                if (a.topic_time < b.topic_time) {
                    return 1;
                } else if (a.topic_time > b.topic_time) {
                    return - 1;
                }
                return 0;
            });
            
            // And now display the first n news
            var res = "";
            for (var i = 0; i < 7; i++) {
                res += "<div class='panel-prefix news-date'>" + formatDate(news[i].topic_time) + "</div><div class='panel-body news-body'>" +
                "<a " + (isLastNews(news[i].topic_time)? "class='last-news'": "") +
                "href='http://www.nntt.org/viewtopic.php?f=" + news[i].forum_id + "&t=" + news[i].topic_id + "' title='" + news[i].topic_title + "'>" + news[i].topic_title + "</a></div>";
            }
            
            $("#news").html(res);
        })
        .fail(function (xhr, status, error) {
            System.Debug.outputString("NNTT.ORG Informer: Unable to get news. Reason: " + xhr.statusText);
            $("#news").html("<span class='center-wrapper'><span><p class='load-message'><img src='images/error.png' class='gadget-icon'/>" + strings[ 'error'] + xhr.statusText + "</p></span></span>");
        });
    }
}

//----------------------------------------
// Working with flyout
//---
function fillFlyoutAndShow(data) {
    if (System.Gadget.Flyout.show) {
        // The window is already open, check if the link is pressed twice
        if (lastOpenedLink == (data.forum_id + "," + data.topic_id)) {
            // Yeah, just close the window
            System.Gadget.Flyout.show = false;
        } else {
            // No, another link was clicked, fill window with new content
            addContentToFlyout(data);
        }
    } else {
        System.Gadget.Flyout.show = true;
        System.Gadget.Flyout.onShow = function () {
            prepairFlyoutWindow();
            addContentToFlyout(data);
        }
    }
}

function prepairFlyoutWindow() {
    try {
        var fd = System.Gadget.Flyout.document;
        
        // Initiate interactive close button
        setHoverEffect($(fd).find("#close-btn img:first"));
        $(fd).find("#close-btn").click(function () {
            System.Gadget.Flyout.show = false;
            return false;
        });
    }
    catch (e) {
    }
}

function addContentToFlyout(data) {
    try {
        var fd = System.Gadget.Flyout.document;
        
        // Fill all fields
        $(fd).find("#status").html(status[data.status] + "&nbsp;" + strings[status_text[data.status]]);
        $(fd).find("#fileRegTime").html(formatFileTime(data.filetime));
        $(fd).find("#release-link").text(data.topic_title).attr("href", "http://www.nntt.org/viewtopic.php?f=" + data.forum_id + "&t=" + data.topic_id);
        $(fd).find("#size").text(getSizeString(data[ "size"]));
        $(fd).find("#downloaded").text(data.completed + strings[ "downloadedTimes"]);
        $(fd).find("#seeders").text(data.seeders);
        $(fd).find("#leechers").text(data.leechers);
        $(fd).find("#down-speed").text(getSizeString(data.speed_down) + "/s");
        $(fd).find("#up-speed").text(getSizeString(data.speed_up) + "/s");
        
        // Calculate flyout body height and set it
        var height = $(fd).find("#content").height() + 20;
        $(fd.body).css('height', height);
        
        // Fix bug with background image height
        $(fd).find("#background").css("height", height - 2);
        
        // Save last link
        lastOpenedLink = data.forum_id + "," + data.topic_id;
    }
    catch (e) {
    }
}

//----------------------------------------
// Helper functions
//---
function formatDate(unixTime) {
    var d = new Date();
    // Conver from unix time
    d.setTime(unixTime * 1000);
    return (d.getDate() + " " + month[d.getMonth()]);
}

function formatFileTime(unixTime) {
    var d = new Date();
    // Conver from unix time
    d.setTime(unixTime * 1000);
    return d.getHours() + ":" + d.getMinutes() + " " + d.getDate() + "." + String(d.getMonth() + 1) + "." + d.getFullYear();
}

function isLastNews(unixTime) {
    // Period in seconds
    var period = parseInt(Settings().get('lastNewsPeriod'));
    var d = new Date();
    return ((d.getTime() * 0.001 - unixTime) < period);
}

function animateText() {
    $("#get-update")
    .animate({
        "opacity": 0.3
    },
    1000)
    .animate({
        "opacity": 1
    },
    1000, animateText);
}

function setHoverEffect(obj) {
    $(obj).each(function (index, item) {
        $(item).hover(function () {
            var src = $(this).attr("src").match("[^\\.]+") + "_hover.png";
            $(this).attr("src", src);
        },
        function () {
            var src = $(this).attr("src").replace("_hover", "");
            $(this).attr("src", src);
        });
    });
}